using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;

namespace SharpCast
{
    [Serializable(), ClassInterface(ClassInterfaceType.AutoDual)]
    public class Channel : Common
    {
        private Uri uri;
        [NonSerialized()]
        private XmlDocument doc;
        private DateTime lastModified;
        private Encoding encoding;

        // Main fields
        private string _title;
        private Uri _link;
        private string _description;
        private DateTime _published;
        private string[] _keywords;
        private string[] _categories;
        private string _copyright;
        private string _summary;

        private Episode[] _episodes;

        // Events
        public delegate void channelUpdateListener(Channel c);
        [field: NonSerialized]
        public event channelUpdateListener channelUpdated;

        public Channel()
        {
            // Default constructor
        }

        public Channel(string url)
        {
            load(url);
        }

        public List<Exception> load(string url)
        {
            this.uri = new Uri(url);
            return update();
        }

        public List<Exception> update()
        {
            List<Exception> errors = new List<Exception>();
            Stream stream = null;
            string url = uri.OriginalString;

            // Compare last modification time (from RSS.NET)
            switch (uri.Scheme)
            {
                case "file":
                    if (!(lastModified == File.GetLastWriteTime(url)))
                    {
                        stream = new FileStream(url, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                        lastModified = File.GetLastWriteTime(url);
                    }
                    break;
                case "https":
                    goto case "http";
                case "http":
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
                    
                    request.IfModifiedSince = lastModified;
                    
                    try
                    {
                        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                        if (!(lastModified == response.LastModified))
                        {
                            try
                            {
                                if (response.ContentEncoding != "")
                                    encoding = Encoding.GetEncoding(response.ContentEncoding);
                            }
                            catch { }
                            stream = response.GetResponseStream();
                            lastModified = response.LastModified;
                        }
                    }
                    catch { }
                    break;
            }

            // Load feed
            if (stream != null)
            {
                doc = new XmlDocument();
                Console.Write("Loading podcast from " + uri.ToString() + "...");
                doc.Load(stream);
                element = getChild(doc.DocumentElement, "channel");

                // Update feed url
                try
                {
                    XmlElement newUri = getChild("itunes:new-feed-url");
                    uri = new Uri(newUri.InnerText);
                }
                catch { }

                // Update main fields
                // TODO: handle errors
                // TODO: add missing fields
                if (title == null)
                    _title = getValue("title");
                try
                {
                    _link = new Uri(getValue("link"));
                }
                catch { }
                _description = getValue("description");
                try
                {
                    _published = DateTime.Parse(getValue("pubDate"));
                }
                catch { }

                _keywords = getValue("itunes:keywords").Split(new char[] {' ', ','}, StringSplitOptions.RemoveEmptyEntries);
                _copyright = getValue("copyright");
                _summary = getValue("itunes:summary");

                // Categories
                XmlNodeList tags = element.GetElementsByTagName("itunes:category");
                _categories = new String[tags.Count];
                for (int i = 0; i < tags.Count; i++)
                {
                    _categories[i] = tags[i].Attributes["text"].InnerText;
                }

                // Episodes
                tags = element.GetElementsByTagName("item");
                List<Episode> eps = new List<Episode>();
                for (int i = 0; i < tags.Count; i++)
                {
                    try
                    {
                        eps.Add(new Episode((XmlElement)tags[i], this));
                    }
                    catch (Exception e) {
                        errors.Add(e);
                    }
                }
                _episodes = eps.ToArray();

                Console.WriteLine("done");

                if (channelUpdated != null)
                    channelUpdated(this);
            }

            return errors;
        }

        /// <summary>
        /// the title of the podcast
        /// </summary>
        /// <returns></returns>
        public string title
        {
            get { return _title; }
            set
            {
                _title = value;
                if (channelUpdated != null)
                    channelUpdated(this);
            }
        }

        public string description
        {
            get { return _description; }
        }

        public Uri link
        {
            get {return _link;}
        }

        /// <summary>
        /// the episodes present in this podcast
        /// </summary>
        /// <returns></returns>
        public Episode[] episodes
        {
            get { return _episodes; }
        }

        public String[] keywords
        {
            get { return _keywords; }
        }

        public String[] categories
        {
            get { return _categories; }
        }
    }
}
